﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace TresselLS
{
    class TextRecord
    {
        /*#
        Variable Name: address
        Module Where Defined: TextRecord
        Data Type: int
        Local or Global: Global
        Purpose: int value of the memory of this TextRecord
        #*/
        private int address;
        /*#
        Variable Name: binCode
        Module Where Defined: TextRecord
        Data Type: string
        Local or Global: Global
        Purpose: contains the binary 24 bits converted from the Hex code.
        #*/
        private  string binCode;
        /*#
        Variable Name: moduleName
        Module Where Defined: TextRecord
        Data Type: string
        Local or Global: Global
        Purpose: contains the name of module where the record is from.
        #*/
        private string moduleName;
        /*#
        Variable Name: relocationCode
        Module Where Defined: TextRocord
        Data Type: char
        Local or Global: Global
        Purpose: char value of relocationCode
        #*/
        private char relocationCode;
        /*#
        Variable Name: effAddr
        Module Where Defined: TextRecord
        Data Type: int
        Local or Global: Global
        Purpose: contains the 11bits effectiveAddress
        #*/
        private int effAddr;
        /*#
        Variable Name: opCode
        Module Where Defined: TextRecord
        Data Type: string
        Local or Global: Global
        Purpose: contain the opCode of this record.
        #*/
        private string opCode;  
        /*#
        Variable Name: tresselLinkerReference
        Module Where Defined: TextRecord
        Data Type: tresselLinker
        Local or Global: Global
        Purpose: a reference to linker object so that we can access information in linker easily
        #*/
        private TresselLinker tresselLinkerReference;
        /*#
        Variable Name: isAdrComp
        Module Where Defined: TextRecord
        Data Type: bool
        Local or Global: Global
        Purpose: a flag bool value to check if current textrecord is an AdrComp
        #*/
        private bool isAdrComp=false;
        /*#
        Variable Name: errorFlag
        Module Where Defined: TextRecord
        Data Type: int
        Local or Global: Global
        Purpose: a flag int value to check if current textrecord has error and what the error message number is.
        #*/
        private int errorFlag = -1;
        /*#
        Variable Name: lineNumber
        Module Where Defined: TextRecord
        Data Type: int
        Local or Global: Global
        Purpose: an int value to hold the line number of when the error occurs.
        #*/
        private int lineNumber = 0;
        /*#
        Variable Name: externals
        Module Where Defined: TextRecord
        Data Type: stringp[
        Local or Global: Global
        Purpose: array value to keep all the exterals, in format [relocation code][+/-][external label name]
        #*/
        private string[] externals = new string[6];

        public string BinCode
        {
            #region preamble
            /*!
            Procedure Name: BinCode
            Description:return the whole 24 bit binary code.
            Input Parameters:
            Output Parameters:
            Error Conditions Tested: 
            Error Messages Generated:
            Original Author: Greg Loesch and Louis Pan
            Procedure Creation Date: 11/20/2009
            Modification Log:
            Who: 
            When: 
            Why: 
            Coding standards met: SR    
            Testing standards met: RM
            !*/
            #endregion
            get
            {
                return binCode;
            }
        }

        public string OpCode
        {
            #region preamble
            /*!
        Procedure Name: OpCode
        Description:
        Input Parameters:
        Output Parameters:
        Error Conditions Tested: 
        Error Messages Generated:
        Original Author: Greg Loesch and Louis Pan
        Procedure Creation Date: 11/18/2009
        Modification Log:
        Who: 
        When: 
        Why: 
        Coding standards met: SR    
        Testing standards met: RM
        !*/
            #endregion

            get
            {
                return opCode;
            }
        }

        public string Get14Bits
        {
            #region preamble
            /*!
            Procedure Name: Get14Bits
            Description:
            Input Parameters:
            Output Parameters:
            Error Conditions Tested: 
            Error Messages Generated:
            Original Author: Greg Loesch and Louis Pan
            Procedure Creation Date: 11/18/2009
            Modification Log:
            Who: 
            When: 
            Why: 
            Coding standards met: SR    
            Testing standards met: RM
            !*/
            #endregion
            get
            {
                return binCode.Substring(10, 14);
            }
        }

        public string RegisterBits
        {
            #region preamble
            /*!
            Procedure Name: RegisterBits
            Description:
            Input Parameters:
            Output Parameters:
            Error Conditions Tested: 
            Error Messages Generated:
            Original Author: Greg Loesch and Louis Pan
            Procedure Creation Date: 11/18/2009
            Modification Log:
            Who: 
            When: 
            Why: 
            Coding standards met: SR
            Testing standards met: RM
            !*/
            #endregion
            get
            {
                return binCode.Substring(6, 3);
            }
        }

        public int Address
        {
            #region preamble
            /*!
            Procedure Name: Address
            Description:  made accessable to address
            Input Parameters:
            Output Parameters:
            Error Conditions Tested: 
            Error Messages Generated:
            Original Author: Greg Loesch and Louis Pan
            Procedure Creation Date: 11/21/2009
            Modification Log:
            Who: 
            When: 
            Why: 
            Coding standards met: SR
            Testing standards met: RM
            !*/
            #endregion
            get
            {
            return address;
            }
        }
       
        public TextRecord(string textRecordLine,TresselLinker tresselLinkerReferenceParam)
        {
            #region preamble
            /*!
            Procedure Name: TextRecord
            Description:
            Input Parameters:
            Output Parameters:
            Error Conditions Tested: 
            Error Messages Generated:
            Original Author: Greg Loesch and Louis Pan
            Procedure Creation Date: 11/18/2009
            Modification Log:
            Who: Louis
            When: 11/19/2009
            Why: add initialization to varibles
            Who: Louis
            When: 11/20/2009
            Why: change check condition to syntax based on converter and tresselLinker class
            Who: Louis
            When: 11/21/2009
            Why: change relocation setting in externals., add length check on original textrecord line checking 
            Who: Greg Loesch
            When: 11/24/2009
            Why: There was an issue with checking the record array length (mod was not a viable option to check array length)
            Who: Greg Loesch
            When: 11/24/2009
            Why: There was a = where a += was needed for the address.
            Who: Louis Pan
            When: 11/29/2009
            Why: add check for external, and move check for module name so module checks before external.
            Coding standards met: SR   
            Testing standards met: RM
            !*/
            #endregion

            binCode = "000000000000000000000000";
            address = -1;
            effAddr = -1;
            relocationCode = 'E';
            moduleName = "";
            errorFlag = -1;
            lineNumber = tresselLinkerReferenceParam.CurrentLineNumber;

            tresselLinkerReference = tresselLinkerReferenceParam;
            /*#
           Variable Name: recordLineArray
           Module Where Defined: TextRecord
           Data Type: string[]
           Local or Global: Global
           Purpose: keep the textrecord line after parse from pips
           #*/
            string[] recordLineArray = textRecordLine.Split(new char[1] { '|' });
            if (!(recordLineArray.Length == 5 || recordLineArray.Length == 7 || recordLineArray.Length == 10 ||
                recordLineArray.Length == 13 || recordLineArray.Length == 16 || recordLineArray.Length == 19))
            {
                errorFlag = 7;
                
                
                return;
            }
            moduleName = recordLineArray[recordLineArray.Length - 1].ToString();

            if (!moduleName.Equals(tresselLinkerReference.CurrentModuleName))
            {
                errorFlag = 33;
                moduleName = tresselLinkerReference.CurrentModuleName;
                return;
            }

            for (int i = 4; i < recordLineArray.Length; i += 3)
            {
                if (!(recordLineArray[i].Equals("+") || recordLineArray[i].Equals("-") || recordLineArray[i].Equals(tresselLinkerReference.CurrentModuleName)))
                {

                    errorFlag = 7;
                    return;
                }
            }
            binCode = Converter.ToBin(recordLineArray[2].ToString());
            if (binCode.Equals("") || recordLineArray[2].Length != 6) // binCode.Length !=24
            {   
                errorFlag = 1;
                binCode = binCode.PadLeft(24, '0');
                return;
            }
            binCode = binCode.PadLeft(24, '0');

            relocationCode = recordLineArray[3][0];
            if (!(new char[4] { 'C', 'B', 'A', 'R' }).Contains(relocationCode) || recordLineArray[3].Length != 1)
            {
                errorFlag = 6;
                
                return;
            }

            opCode = tresselLinkerReference.GetOpCode(binCode.Substring(0, 6));
            if (opCode.Equals(""))
            {
                if (relocationCode.Equals('A'))
                {
                    opCode = "ERROR";
                }
                else
                {
                    errorFlag = 2;
                    
                    return;
                }
            }
            
            address = Converter.ToInt(recordLineArray[1].ToString(),false);
            if (address == -1 || recordLineArray[1].ToString().Length != 4)
            {
                errorFlag = 3;
                
                return;
            }
            
            effAddr = Converter.ToInt(Converter.ToHex(binCode.Substring(13)),false);


            


            if (textRecordLine.Contains('+') || textRecordLine.Contains('-'))
            {
                isAdrComp = true;
                relocationCode = 'C';
                for (int i = 3; i < recordLineArray.Length - 2; i += 3)
                {
                    try
                    {
                        externals[(i - 3) / 3] = recordLineArray[i].ToString() + recordLineArray[i + 1].ToString() + recordLineArray[i + 2].ToString();
                    }
                    catch
                    {
                        errorFlag = 5;

                        return;
                    }
                }
            }
            //Error checking if any, set to nop and return
        }

        public void parse()
        {
            #region preamble
            /*!
            Procedure Name: parse()
            Description:
            Input Parameters:
            Output Parameters:
            Error Conditions Tested: 
            Error Messages Generated:
            Original Author: Greg Loesch and Louis Pan
            Procedure Creation Date: 11/18/2009
            Modification Log:
            Who: Louis 
            When: 11/21/2009
            Why: set location based on relocation code and module adjustment.
            Coding standards met: SR   
            Testing standards met: RM
            !*/
            #endregion
            if (errorFlag > 0)
            {
                SetToNop(errorFlag);
                return;
            }
            switch (relocationCode)
            {

                case 'R':
                {
                    effAddr = effAddr + tresselLinkerReference.GetValueFromSymbolTable(moduleName, "ADJUSTMENT");
                }
                break;
                case 'C':
                {
                    for (int i = 0; i < externals.Length && externals[i] != null; i++)
                    {
                        if (externals[i][0].Equals('R'))
                        {
                            if (externals[i][1].Equals('+'))
                            {
                                effAddr = effAddr + tresselLinkerReference.GetValueFromSymbolTable(moduleName, "ADJUSTMENT");
                            }
                            else if (externals[i][1].Equals('-'))
                            {
                                effAddr = effAddr + tresselLinkerReference.GetValueFromSymbolTable(moduleName, "ADJUSTMENT");
                            }
                            else
                            {
                                SetToNop(20);
                                return;
                            }

                        }
                        else if(externals[i][0].Equals('C'))
                        {
                            if (tresselLinkerReference.IsInSymbolTable(externals[i].Substring(2, externals[i].Length - 2)).Equals("Entry_Sym"))
                            {
                                if (externals[i][1].Equals('+'))
                                {
                                    effAddr = effAddr + tresselLinkerReference.GetValueFromSymbolTable(externals[i].Substring(2, externals[i].Length - 2), "LINKER ADDRESS");
                                }
                                else if (externals[i][1].Equals('-'))
                                {
                                    effAddr = effAddr - tresselLinkerReference.GetValueFromSymbolTable(externals[i].Substring(2, externals[i].Length - 2), "LINKER ADDRESS");
                                }
                                else
                                {
                                    SetToNop(20);
                                    return;
                                }
                            }
                            else
                            {
                                SetToNop(19);
                                return;
                            }
                        }
                        else
                        {
                            errorFlag = 6;
                            lineNumber = tresselLinkerReference.CurrentLineNumber;
                            SetToNop(6);
                            return;
                        }
                    }

                }
                break;
                case 'A':
                { 
                }
                break;
                case 'B':
                {
                }
                break;
            }
            if(effAddr>2047)
            {
                SetToNop(14);
                tresselLinkerReference.FatalErrorFlag = true;
                return;
            }
            if(effAddr<0)
            {
                SetToNop(15);
                return;
            }
            address += tresselLinkerReference.GetValueFromSymbolTable(moduleName, "ADJUSTMENT");

            if (address < tresselLinkerReference.CurrentLocationCounter)
            {
                SetToNop(4);
                return;
            }
            else
            {
                tresselLinkerReference.CurrentLocationCounter = address+1;
            }

            binCode = binCode.Substring(0, 13) + Converter.ToBin(effAddr).Substring(13,11);

            

        }
        
        public void SetToNop(int errorNumber)
        {
            #region preamble
            /*!
            Procedure Name: SetToNop
            Description:
            Input Parameters:
            Output Parameters:
            Error Conditions Tested: 
            Error Messages Generated:
            Original Author: Greg Loesch and Louis Pan
            Procedure Creation Date: 11/18/2009
            Modification Log:
            Who: Louis  
            When: 11/21/2009
            Why: add reset value to effaddr, moduleName, address and effaddr = 0;
            Coding standards met: SR    
            Testing standards met: RM
            !*/
            #endregion
            opCode = "SLA-AC";
            relocationCode = 'A';
            binCode = "011110" + binCode.Substring(6, 7) + "00000000000";
            effAddr = 0;
            address = tresselLinkerReference.CurrentLocationCounter;
            tresselLinkerReference.CurrentLocationCounter++;
            effAddr = 0;
            externals = new string[0];
            tresselLinkerReference.AddError(errorNumber, lineNumber);
            //add error to linkerReference


        }
        
        public string Text()
        {
            #region preamble
            /*!
            Procedure Name: Text
            Description:
            Input Parameters:
            Output Parameters:
            Error Conditions Tested: 
            Error Messages Generated:
            Original Author: Greg Loesch and Louis Pan
            Procedure Creation Date: 11/18/2009
            Modification Log:
            Who: 
            When: 
            Why: 
            Coding standards met: SR    
            Testing standards met: RM
            !*/
            #endregion
            return ("LT|"+ Converter.ToHex(address).ToString()+"|"+Converter.ToHex(binCode).ToString().PadLeft(6, '0')+"|"+moduleName);
        }





    }
}
